package io.cici.cc.mybatis.lite.binding;

import io.cici.cc.mybatis.lite.executor.resultset.ResultContextHandler;
import io.cici.cc.mybatis.lite.reflection.ParameterResolver;
import io.cici.cc.mybatis.lite.reflection.TypeParameterResolver;
import io.cici.cc.mybatis.lite.session.Configuration;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * @author huihui
 */
@Slf4j
@Setter
@Getter
public class MethodReturnTypeSignature {
    private final boolean returnsVoid;
    private final Class<?> returnType;
    private final boolean returnsMany;
    private final Integer resultHandlerIndex;

    private final ParameterResolver parameterResolver;

    public MethodReturnTypeSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
        Type resolvedReturnType = TypeParameterResolver.resolveReturnType(mapperInterface, method);
        if (resolvedReturnType instanceof Class<?>) {
            this.returnType = (Class<?>) resolvedReturnType;
        } else if (resolvedReturnType instanceof ParameterizedType) {
            this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
        } else {
            this.returnType = method.getReturnType();
        }
        this.returnsVoid = void.class.equals(this.returnType);
        this.returnsMany = Configuration.objectFactory.isCollection(this.returnType);

        this.resultHandlerIndex = getUniqueParamIndex(method, ResultContextHandler.class);
        this.parameterResolver = new ParameterResolver(configuration, method);
    }

    private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
        Integer index = null;
        final Class<?>[] argTypes = method.getParameterTypes();
        for (int i = 0; i < argTypes.length; i++) {
            if (paramType.isAssignableFrom(argTypes[i])) {
                if (index != null) {
                    throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
                }
                index = i;
            }
        }
        return index;
    }

    public boolean hasResultHandler() {
        return resultHandlerIndex != null;
    }

    public ResultContextHandler extractResultHandler(Object[] args) {
        return hasResultHandler() ? (ResultContextHandler) args[resultHandlerIndex] : null;
    }

    public Object convertArgsToSqlCommandParam(Object[] args) {
        return parameterResolver.getNamedParams(args);
    }


}
